﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Xml.XPath;

namespace PriceUpdater
{
    class Program
    {
        private const string EVE_MARKETEER = "http://www.evemarketeer.com/api/info/{0}/xml/10000002/sell_avg";
        private const string EVE_CENTRAL = "http://api.eve-central.com/api/marketstat?typeid={0}&regionlimit=10000002";
        private const string PRICE_FACTION = "http://prices.c0rporation.com/faction.xml";

        private const string EC_XPATH = "/evec_api/marketstat/type/sell/median";
        private const string EM_XPATH = "/result/row/sell_avg";
        private const string PF_XPATH = "/eveapi/result/rowset/row";

        static void Main(string[] args)
        {
            try
            {
                UpdateFactionItems();
            }
            catch (Exception exp)
            {
                Console.WriteLine("Failed to update faction items prices. Exception: {0}. Details: {1}", exp.GetType(), exp.Message);
            }

            //UpdatePrices();
        }

        static void UpdatePrices()
        {
            
            IEnumerable<int> items = null;
            using(var cnn = new SqlConnection(Properties.Settings.Default.DbConnection) )
            {
                cnn.Open();

                items = GetItemIds(cnn);
                if(items == null || !items.Any())
                {
                    cnn.Close();
                    return;
                }
                cnn.Close();
            }
            UpdatePrices(2, items);
        }


        static void UpdateFactionItems()
        {
            Console.WriteLine("Updating faction items value");
            int count = 0;
            var request = WebRequest.Create(PRICE_FACTION) as HttpWebRequest;
            if (request == null) return;
            request.KeepAlive = false;

            var response = request.GetResponse() as HttpWebResponse;
            if(response ==null || response.StatusCode != HttpStatusCode.OK)
            {
                Console.WriteLine("No response or http status code invalid from " + request.RequestUri);
                return;
            }

            using(var st = response.GetResponseStream())
            {
                var xdoc = new XPathDocument(st);
                var xnav = xdoc.CreateNavigator();

                var nodes = xnav.Select(PF_XPATH);
                while (nodes.MoveNext())
                {
                    if(nodes.Current ==null) continue;

                    var value = nodes.Current.GetAttribute("median", "");
                    if(value ==null || string.IsNullOrEmpty(value)) continue;

                    var item = nodes.Current.GetAttribute("typeID", "");
                    if (item == null || string.IsNullOrEmpty(item)) continue;

                    decimal price = 0;
                    if(!decimal.TryParse(value,NumberStyles.Any, CultureInfo.InvariantCulture, out price)) continue;

                    int itemId = 0;
                    if(!int.TryParse(item, out itemId)) continue;


                    UpdateItemPrice(itemId, 1, price);
                    count++;
                }
            }
            Console.WriteLine(string.Format("{0} faction item(s) updated", count));
        }

        static IEnumerable<int> GetItemIds(SqlConnection cnn)
        {
            IList<int> itemIds = new List<int>();
            using (var cmd = cnn.CreateCommand())
            {
                cmd.CommandText =
                    @"select distinct I.ID
                    from Items I
                    left join ItemValues V on V.ItemID = I.ID
                    where 
                    V.ItemValue is null OR
                    DATEDIFF(HOUR,V.Updated, GETDATE()) > 24";

                using (var result = cmd.ExecuteReader())
                    while (result.Read())
                    {
                        itemIds.Add((int)result["ID"]);
                    }
            }
            return itemIds;
        }

        static void UpdatePrices(short source, IEnumerable<int> items)
        {
            Console.WriteLine("Updating regular items prices");

            string uri = null;
            string xpath = null;
            switch (source)
            {
                case 2:
                    uri = EVE_CENTRAL;
                    xpath = EC_XPATH;
                    break;
                case 3 :
                    uri = EVE_MARKETEER;
                    xpath = EM_XPATH;
                    break;
            }
            if(string.IsNullOrEmpty(uri)) return;

            Console.WriteLine(string.Format("{0} items to update", items.Count()));

            var tasks = new Task[items.Count()];
            var index = 0;
            foreach (var id in items)
            {
                var itemId = id;
                tasks[index] = Task.Factory.StartNew( () => UpdateItemPrice(itemId, source, GetPriceFromSource(itemId, uri, xpath)) );
                index++;
            }
            Task.WaitAll(tasks);


            Console.WriteLine(string.Format("Items prices update from source with id \"{0}\" completed.", source));
        }

        private const string CMD_UPDATE = @"
if exists (select 1 from ItemValues where ItemID = {0} and SourceID = {1})
    update ItemValues
    set ItemValue = {2}, Updated = GETDATE()
    where ItemID = {0} and SourceID = {1}
else
    Insert into ItemValues (ItemID, ItemValue, SourceID) values ({0}, {2}, {1})";


        static decimal GetPriceFromSource(int itemId, string uri, string xpath)
        {
            var request = WebRequest.Create(string.Format(uri, itemId)) as HttpWebRequest;
            if (request == null) return 0;
            request.KeepAlive = false;

            decimal price = 0;
            try
            {
                var response = request.GetResponse() as HttpWebResponse;
                if (response == null || response.StatusCode != HttpStatusCode.OK) return 0;

                var xdoc = new XPathDocument(response.GetResponseStream());
                var xnav = xdoc.CreateNavigator();

                var node = xnav.SelectSingleNode(xpath);
                response.Close();
                if (node == null) return 0;

                price = string.IsNullOrEmpty(node.Value) || string.Equals("null", node.Value)
                    ? 0
                    : Math.Round(decimal.Parse(node.Value, CultureInfo.InvariantCulture), 2);

                Console.WriteLine(string.Format("Item \"{0}\" price retrieved from \"{1}\"", itemId, string.Format(uri, itemId)));
            }
            catch (Exception exp)
            {
                Console.WriteLine(string.Format("Failed to update item price with id \"{0}\" from \"{3}\". Exception \"{1}\". Details: {2}",
                            itemId,
                            exp.GetType(),
                            exp.Message,
                            string.Format(uri, itemId)));
            }

            
            return price;
        }

        static void UpdateItemPrice(int itemId, int sourceId, decimal price)
        {
            using (var cnn = new SqlConnection(Properties.Settings.Default.DbConnection))
            using (var cmd = cnn.CreateCommand())
            {
                cnn.Open();
                cmd.CommandText = string.Format(CultureInfo.InvariantCulture, CMD_UPDATE, itemId, sourceId, price);
                try
                {
                    cmd.ExecuteNonQuery();
                    //Console.WriteLine(string.Format("Item \"{0}\" updated from source \"{1}\"", itemId, sourceId));
                }
                catch (Exception exp)
                {
                    Console.WriteLine(string.Format("Failed to update item \"{0}\" price to database. Exception \"{1}\". Details: {2}",
                        itemId,
                        exp.GetType(),
                        exp.Message ));
                }
            }
        }
    }
}


/*
<result>
    <row>
    <type_id>34</type_id>
    <region_id>10000002</region_id>
    <datetime>2012-02-25 22:55:43</datetime>
    <sell_avg>4.6417883100729</sell_avg>
    </row>
</result>
 
 */